import {toastValidateError} from "./tools";

const {StatusBar, Platform, DeviceEventEmitter} = require('react-native');
const moment = require('moment');

const {Toast} = require('antd-mobile');
import constant from "./constant";
import Validator from "./Validator";
import RouterServer from "./RouterServer";
import T from "./T";

let instance = null;


export interface API {
    HOST: string,
}


/**
 * 请求
 */
class Request {

    /**
     * 接口对象
     */
    Api: API;

    /**
     * 是否是debug模式
     */
    debug: boolean;

    /**
     * 请求超时 默认 30*1000(ms)
     */
    timeout: number;

    errorOptions: Object;

    /**
     * 接口
     * @return {(() => (() => any) | API | any) | API | any}
     * @constructor
     */
    static getApi() {
        return Request.getInstance().Api;
    };

    /**
     * 初始化
     * @param {API} api
     * @param {boolean} debug
     * @param {number} timeout
     * @param {Object}  errorOptions
     */
    static init(api: API, debug = true, timeout = 30 * 1000, errorOptions = {}) {
        Request.getInstance().Api = api;
        Request.getInstance().debug = debug;
        Request.getInstance().timeout = timeout;
        Request.getInstance().errorOptions = errorOptions;
    }

    /**
     *
     * @return Request
     */
    private static getInstance() {
        if (!instance) {
            instance = new Request();
        }
        return instance;
    }

    /**
     * 参数拼接 将对象拼接成 key=value&key=value 的形式
     * @param {Object} params
     * @return {string}
     */
    private static makeQueryString = (params?: object) => {
        if (!params) {
            return ''
        }
        let query = [];
        for (let key in params) {
            if (key === 'Authorization') continue;
            if (key === 'Content-Type') continue;
            if (!params.hasOwnProperty(key)) {
                continue
            }
            query.push(`${key}=${params[key]}`)
        }
        if (query.length > 0) {
            return '?' + query.join('&')
        } else {
            return '';
        }
    };

    /**
     * 拼接表单参数
     * @param {Object} params
     * @return {FormData}
     */
    private static makeFormData = (params: object) => {
        const formData = new FormData();
        for (let key in params) {
            if (!params.hasOwnProperty(key)) {
                continue
            }
            if (key === 'Authorization') continue;
            if (key === 'Content-Type') continue;
            if (key === 'params') {
                return JSON.stringify(params['params']);
            }
            formData.append(key, params[key])
        }
        return formData
    };


    /**
     * 检查响应
     * @param {Response} response
     * @return {Promise<Response>}
     */
    private static checkResponse(response: Response) {
        Request.setNetworkActivityIndicatorVisible(false);
        // Request.log(response.status);
        return response.json().then(d => d).catch(e => true);
    };


    /**
     * 拼接请求头
     * @param params
     * @param timestamp
     * @return {{timestamp: any}}
     */
    private static makeHeaders = (params, timestamp?: string) => {

        if (params && params['Authorization']) {
            return new Headers({
                "Content-Type": params['Content-Type'] ? params['Content-Type'] : "application/x-www-form-urlencoded",
                'Authorization': params['Authorization']
            })
        } else if (params && params['Content-Type']) {
            return new Headers({
                "Content-Type": params['Content-Type'] ? params['Content-Type'] : "application/x-www-form-urlencoded",
            })
        }
        return null;

    };

    /**
     * 打印错误信息
     * @param {Error} e
     * @param from
     * @constructor
     */
    private static LogError = (e: Error, from: any = null) => {
        if (from) {
            Request.log('%c request error ,the error from:', 'color:red', from, e);
        } else {
            Request.log('%c request error ', 'color:red', e);
        }
        if (e.message.toLowerCase().indexOf('unexpected token') !== -1) {
            toastValidateError(null, T('Server error'))
        } else {
            toastValidateError(null, e.message)
        }
        return false;
    };

    /**
     * 日志
     * @param args
     */
    static log = (...args) => {
        if (Request.getInstance().debug) {
            console.log(...args);
        }
    };

    /**
     * POST请求
     * @param {string} url
     * @param {Object} params
     * @param {boolean} inApp
     * @return {Promise<any | any | any>}
     */
    static post(url?: string, params?: Object, inApp: boolean = true) {
        Request.setNetworkActivityIndicatorVisible(true);
        const apiUrl = inApp ? Request.getInstance().Api.HOST + url : url;
        return Request.timeout(Request.getInstance().timeout, fetch(apiUrl, {
            method: 'POST',
            headers: Request.makeHeaders(params),
            body: Request.makeFormData(params)
        })).then((data: Response) => {
            Request.setNetworkActivityIndicatorVisible(false);

            if (data.ok === true) {
                return Request.checkResponse(data);
            } else {
                return data.json().then(Request.errorOption);
            }
        }).then((data) => {
            Request.log('%c 网络请求响应 ', 'color:blue', apiUrl, data);
            return data
        }).catch((e: Error) => {
            Request.setNetworkActivityIndicatorVisible(false);
            Request.LogError(e, url);
            return false;
        });
    }

    /**
     * DELETE请求
     * @param {string} url
     * @param {Object} params
     * @param {boolean} inApp
     * @return {Promise<any | any | any>}
     */
    static del(url?: string, params?: Object, inApp: boolean = true) {
        Request.setNetworkActivityIndicatorVisible(true);
        const apiUrl = inApp ? Request.getInstance().Api.HOST + url+Request.makeQueryString(params)  : url;
        return Request.timeout(Request.getInstance().timeout, fetch(apiUrl, {
            method: 'DELETE',
            headers: Request.makeHeaders(params),
            // body: Request.makeFormData(params)
        })).then((data: Response) => {
            Request.setNetworkActivityIndicatorVisible(false);
            if (data.ok === true) {
                return Request.checkResponse(data);
            } else {
                return data.json().then(Request.errorOption);
            }
        }).then((data) => {
            Request.log('%c 网络请求响应 ', 'color:blue', apiUrl, data);
            return data
        }).catch((e: Error) => {
            Request.setNetworkActivityIndicatorVisible(false);
            Request.LogError(e, apiUrl);
            return false;
        });
    }

    /**
     * PUT请求
     * @param {string} url
     * @param {Object} params
     * @param {boolean} inApp
     * @return {Promise<any | any | any>}
     */
    static put(url?: string, params?: Object, inApp: boolean = true) {
        Request.setNetworkActivityIndicatorVisible(true);
        const apiUrl = inApp ? Request.getInstance().Api.HOST + url : url;
        return Request.timeout(Request.getInstance().timeout, Request.timeout(Request.getInstance().timeout, fetch(apiUrl, {
            method: 'PUT',
            headers: Request.makeHeaders(params),
            body: Request.makeFormData(params)
        }))).then((data: Response) => {

            Request.setNetworkActivityIndicatorVisible(false);
            if (data.ok === true) {
                return Request.checkResponse(data);
            } else {
                return data.json().then(Request.errorOption);
            }
        }).then((data) => {
            Request.log('%c 网络请求响应 ', 'color:blue', apiUrl, data);
            return data
        }).catch((e: Error) => {
            Request.setNetworkActivityIndicatorVisible(false);
            Request.LogError(e, apiUrl);
            return false;
        });
    }

    /**
     * GET请求
     * @param {string} url 地址
     * @param {Object} params 参数
     * @param {boolean} inApp 是否是 app内访问
     * @return {Promise<any | any | any>}
     */
    static get(url: string, params?: object, inApp: boolean = true) {
        Request.setNetworkActivityIndicatorVisible(true);
        let apiUrl = inApp ? Request.getInstance().Api.HOST + `${url}` + Request.makeQueryString(params) : `${url}` + Request.makeQueryString(params);
        apiUrl = encodeURI(apiUrl);
        return Request.timeout(Request.getInstance().timeout, fetch(apiUrl, {
            method: 'GET',
            headers: Request.makeHeaders(params),
        })).then((data: Response) => {
            Request.setNetworkActivityIndicatorVisible(false);
            if (data.ok === true) {
                return Request.checkResponse(data);
            } else {
                return data.json().then(Request.errorOption);
            }
        }).then((data) => {
            Request.log('%c 请求成功', 'color:green', apiUrl, data);
            return data
        }).catch((e: Error) => {
            Request.setNetworkActivityIndicatorVisible(false);
            Request.LogError(e, apiUrl);
            return false;
        });
    }

    /**
     * 检查网络图片地址
     * @param {string | {uri: string}} url
     * @return {string|object}
     */
    static checkImageUrl(url: string | { uri: string }) {
        if (!url) return '';
        if (typeof url === 'string') {
            if (Validator.validator(url, Validator.urlReg)) {
                return url
            } else {
                return Request.getInstance().Api.HOST + url;
            }
        }
        if (Validator.validator(url.uri, Validator.urlReg)) {
            return url;
        } else {
            return {
                ...url, uri: Request.getInstance().Api.HOST + url.uri
            }
        }
    }

    /**
     * 网络活动指示器
     * @param {boolean} active
     */
    private static setNetworkActivityIndicatorVisible(active: boolean) {
        if (Platform.OS === 'ios') {
            StatusBar.setNetworkActivityIndicatorVisible(active);
        }
    }

    /**
     * promise 超时
     * @param {number} ms
     * @param {Promise<any>} promise
     * @return {Promise<any>}
     */
    private static timeout = (ms: number, promise: Promise<any>) => {
        if (ms <= 0) {
            return promise;
        } else {
            return new Promise(function (resolve, reject) {
                setTimeout(function () {
                    reject(new Error(T('Network request time out') + ''))
                }, ms);
                promise.then(resolve, reject)
            })
        }
    };
    private static errorOption = (data) => {
        if (data.error && data.error) {
            const error: string = data.error;
            if (typeof  Request.getInstance().errorOptions[error] && typeof  Request.getInstance().errorOptions[error] === 'function') {
                typeof  Request.getInstance().errorOptions[error](data);
            }
            throw new Error(error + '');
        } else if (data.message) {
            throw new Error(data.message);
        } else {
            throw new Error(T('Can not connect to server') + '')
        }
    }
}

export default Request;